#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/wait.h>
#include <pthread.h>
#include "thread_pool.hpp"
#include "Task.hpp"

using namespace ns_threadpool;
using namespace ns_task;

void Usage(std::string proc)
{
    std::cout << "Usage:" << proc << "port" << std::endl;
}

// void ServiceIO(int new_sock)
// {
//     //死循环提供服务
//     while (true)
//     {
//         char buffer[1024];
//         memset(buffer, 0, sizeof(buffer));
//         ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
//         if (s > 0)
//         {
//             buffer[s] = 0; //把获取的内容当成字符串
//             //打印获取到的信息
//             std::cout << "client#" << buffer << std::endl;

//             //服务端获取信息后回显给客户端
//             std::string echo_string = ">>>server<<<,";
//             echo_string += buffer;

//             write(new_sock, echo_string.c_str(), echo_string.size());
//         }
//         else if (s == 0)
//         {
//             std::cout << "client quit ..." << std::endl;
//             break;
//         }
//         else
//         {
//             std::cerr << "read error." << std::endl;
//             break;
//         }
//     }
// }

// void *HandlerRequest(void *args)
// {
//     //线程分离
//     pthread_detach(pthread_self());
//     int sock = *(int *)(args);
//     delete (int *)args;

//     ServiceIO(sock);
//     close(sock);
// }

//.tcp_server 8080
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1、创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        std::cerr << "socket error:" << errno << std::endl;
        return 2;
    }

    //在linux中父进程忽略子进程SIGCHLD信号，子进程会自动退出释放资源
    signal(SIGCHLD, SIG_IGN);

    // 2、bind:绑定IP和端口
    struct sockaddr_in local;
    //利用memset清空结构体，初始化为0
    memset(&local, 0, sizeof(local));

    // local.sin_family表示协议家族，这里用的是IPV4协议
    local.sin_family = AF_INET;
    //先将端口号由字符串类型转整型，再将主机序列转网络序列
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;

    if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
        std::cerr << "bind error:" << errno << std::endl;
        return 3;
    }

    // 3、因为TCP是面向连接的，a.在通信前，需要建连接 b.然后才能通信
    //一定有人主动建立（客户端，需要服务），一定有人被动接受连接（服务器，提供服务）
    //当前是server端，需要周而复始的等待客户连接
    //所以需要给用户提供一个建立连接的功能

    //设置套接字是listen状态，本质是允许用户连接
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        std::cerr << "listen error:" << errno << std::endl;
        return 4;
    }

    for (;;)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock < 0)
        {
            continue;
        }

        //客户端发过来的是网络序列，需要转主机序列
        uint16_t cli_port = ntohs(peer.sin_port);
        std::string cli_ip = inet_ntoa(peer.sin_addr);

        std::cout << "get a new link -> : [" << cli_ip << ":" << cli_port << "]#" << new_sock << std::endl;

        //最终版本：线程池版本
        //多进程和多线程创建无上限会出现问题
        //当客户链接来的时候才给客户创建进程和线程，效率低
        // 1、构建一个任务
        Task t(new_sock);
        // 2、将任务push到后端的线程池即可
        //先调用GetInstance()获取单例，然后调用PushTask()放入任务
        ThreadPool<Task>::GetInstance()->PushTask(t);

        //多线程版本
        // pthread_t tid;
        // int *pram = new int(new_sock);
        // //新线程和主线程共享同一份文件描述符表，所以新线程不能关闭listen_sock
        // //否则主线程会受到影响
        // pthread_create(&tid, nullptr, HandlerRequest, (void *)pram);

        //多进程版本
        // pid_t id = fork();
        // if (id < 0)
        // {
        //     continue;
        // }
        // else if (id == 0)
        // {
        //     // child
        //     //子进程会继承父进程打开的文件描述符
        //     //但子进程不需要listen_sock，所以可以直接关闭，不会影响父进程
        //     close(listen_sock);

        //     if (fork() > 0)
        //     {
        //         exit(0); //退出的是子进程
        //     }
        //     //下面这段就是孙子进程走的
        //     ServiceIO(new_sock);
        //     //如果不关闭不需要的文件描述符，会造成文件泄露的问题
        //     close(new_sock);
        //     exit(0);
        // }
        // else
        // {
        //     // father
        //     // do nothing!
        //     //子进程已经继承了new_sock，父进程已经不需要了，所以可以直接关闭
        //     waitpid(id, nullptr, 0);
        //     close(new_sock);
        // }

        //单进程版本，一次只能给一个人提供服务
        // ServiceIO(new_sock);
    }

    return 0;
}